/* freeircd chanserv.c
 *
 * Iniciado em 23/09/2006
 */

#define _GNU_SOURCE
#include <errno.h>
#include "misc.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
#include <time.h>
#include <stdarg.h>
#include <dirent.h>

#include "ggets.h"
#include "types.h"
#include "configuration.h"
#include "getinfo.h"
#include "alloc.h"

#include "debug.h"

#define HELP_UNKNOWN_ACCESS_COMMAND -2
#define HELP_UNKNOWN_COMMAND -1
#define HELP_MAIN 0
#define HELP_REGISTER 1
#define HELP_IDENTIFY 2
#define HELP_INFO 3
#define HELP_ACCESS 4
#define HELP_ACCESS_ADD 5
#define HELP_ACCESS_DEL 6
#define HELP_ACCESS_LIST 7
#define HELP_OP 8
#define HELP_DEOP 9
#define HELP_INVITE 10
#define HELP_KICK 11
#define HELP_UNBAN 12

static void show_chanserv_help(CLIENTS *, veryshort);
static void chanserv_register(CLIENTS *, CHANS **);
static void chanserv_identify(CLIENTS *, CHANS **);
static void chanserv_info(CLIENTS *, CHANS **);
static void chanserv_access(CLIENTS *, CHANS **, USERREG *);
static void chanserv_opdeop(CLIENTS *, CHANS **, USERREG *, bool);
static void chanserv_access(CLIENTS *, CHANS **, USERREG *);
static void chanserv_kick(CLIENTS *, CHANS **, CLIENTS **, long *);
static void chanserv_unban(CLIENTS *, CHANS *);
void chanserv(CLIENTS *, CHANS **, USERREG *, CLIENTS **, long *);
static bool chanserv_iscommand(char *);
bool is_akicked(CLIENTS *, struct chan_akicks *);
veryshort get_user_access_level(struct access_users *, char *);
struct access_users *get_user_access(struct access_users *, char *);
void save_chanreg(CHANS *);

extern void write_sock(CLIENTS *, char *, ...);
extern void send_to_all_in_chan(CLIENTS *client, char *chan, char *str, ...);
extern inline void memerror(void *);
extern void part(CLIENTS *, CHANS **, bool, long *, bool);
extern inline char *get_irc_str(char *, int);
extern void unset_ban(CLIENTS *, CHANS *, char *, char *, bool);

static void show_chanserv_help(CLIENTS *client, veryshort flag)
{
        switch (flag)
        {
                case HELP_MAIN:
                {

                        write_sock(client,":%s!%s@%s NOTICE %s :O serviço chanserv permite registes e controles o \r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :teu canal, prevenindo abusos por outros utilizadores.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Se o canal não for usado por um periodo\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :de %d dias, o registo  do canal e' apagado.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,REMOVE_CHAN_TIMEOUT);
                        write_sock(client,":%s!%s@%s NOTICE %s :Os comandos que se seguem, permitem o registo e configuração\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :do canal; para usa-los escreve: /%s <comando>\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        write_sock(client,":%s!%s@%s NOTICE %s :Para obter mais informações sobre um comando especifico\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :escreve: /%s help <comando>.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        write_sock(client,":%s!%s@%s NOTICE %s :REGISTER Regista um canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :IDENTIFY Identifica o founder do canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :SET Altera as opções e informações do canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :ACCESS Modifica a lista de users privilegiados\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :LEVELS Redefine os níveis de acesso\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :AKICK Faz a manutenção da Lista de Autokick\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :DROP Remove o registo do canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :INFO Mostra a informação de um canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :INVITE Convida um utilizador para o canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :OP Da' op a um utilizador do canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :DEOP Tira o op a um utilizador do canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :UNBAN Tira um ban do canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :BANLIST Lista os bans do canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :USERLIST Lista os utilizadores do canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :KICK Kicka um utilizador do canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :CLEAR \"Limpa\" alguns atributos do canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);

                        break;
                }
                case HELP_REGISTER:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Comando register: Serve para registares um canal.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s register <#canal> <password> <descrição>\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        write_sock(client,":%s!%s@%s NOTICE %s :Nota: A password e' case sensitive\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        break;
                }
                case HELP_IDENTIFY:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Comando identify: Serve para te identificares founder de um canal.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s identify <#canal> <password>\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        break;
                }
                case HELP_INFO:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Comando info: Serve para saberes infomação do registo de um canal.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s info <#canal>\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        break;
                }
                case HELP_ACCESS:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :"
                                          "Comando access: Serve para modificar a lista de users privilegiados de um canal\r\n",
                                          CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s access <comando> <#canal> [...]\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        write_sock(client,":%s!%s@%s NOTICE %s :Para obter mais informações sobre um comando especifico\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :escreve: /%s help access <comando>\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        write_sock(client,":%s!%s@%s NOTICE %s :Comandos:\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :ADD Adiciona um utilizador a' lista de accesso de um canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :DEL Apaga um utilizador da lista de accesso de um canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :LIST Lista os utilizadores da lista de accesso de um canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        break;
                }
                case HELP_ACCESS_ADD:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Comando access add: "
                                          "Serve para adiciona um utilizador a' lista de accesso de um canal\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s access <#canal> add <nick> <nivel>\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        break;
                }
                case HELP_ACCESS_DEL:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Comando access del: "
                                        "Serve para apagar um utilizador da lista de accesso de um canal\r\n",
                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s access <#canal> del <nick>\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        break;
                }
                case HELP_ACCESS_LIST:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Comando access list: "
                                        "Serve para listar os utilizadores da lista de accesso de um canal\r\n",
                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s access <#canal> list\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        break;
                }
                case HELP_UNKNOWN_ACCESS_COMMAND:
                {
                        char *cmd=getoken(client->msg,4,' ');
                        write_sock(client,":%s!%s@%s NOTICE %s :Help: Comando access %s desconhecido\r\n",
                                   NICKSERV_NAME,USER_NAME,SERVER_NAME,client->nick,cmd);
                        write_sock(client,":%s!%s@%s NOTICE %s :Help: Para mais informações escreve: /%s help access\r\n",
                                   NICKSERV_NAME,USER_NAME,SERVER_NAME,client->nick,NICKSERV_NAME);
                        free(cmd);
                        break;
                }
                case HELP_OP:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Comando op: Serve para dar op a um utilizador de um canal.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s op <#canal> [nick]\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        break;
                }
                case HELP_DEOP:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Comando deop: Serve para tirar op a um utilizador de um canal.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s deop <#canal> [nick]\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        break;
                }
                case HELP_INVITE:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Comando invite: Serve para convidar um utilizador para um canal.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s invite <#canal> <nick>\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        break;
                }
                case HELP_KICK:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Comando kick: Serve para kickar um utilizador de um canal.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s kick <#canal> <nick> [motivo]\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        break;
                }
                case HELP_UNBAN:
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Comando unban: Serve para tirar um ban de um canal.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Sintaxe: /%s unban <#canal> [ban_mask]\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                        write_sock(client,":%s!%s@%s NOTICE %s :A ban mask implicita e' o teu nick e o teu ip\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        break;
                }
                case HELP_UNKNOWN_COMMAND:
                {
                        char *cmd=getoken(client->msg,3,' ');
                        write_sock(client,":%s!%s@%s NOTICE %s :Help: Comando %s desconhecido\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,cmd);
                        write_sock(client,":%s!%s@%s NOTICE %s :Help: Para mais informações escreve: /%s help\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,NICKSERV_NAME);
                        free(cmd);
                        break;
                }
        }
}

static void chanserv_register(CLIENTS *client, CHANS **ppchans)
{
        char *channame;
        char *password;
        CHANS *chan;
        struct chan_users_list *user;
        int px=0;
        int counter=0;
        int s_counter=0;

        if (tknum(client->msg,' ') == 2) {
                show_chanserv_help(client,HELP_REGISTER);
                return;
        }

        if (tknum(client->msg,' ') < 5) {
                write_sock(client,":%s!%s@%s NOTICE %s :Register: Poucos parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                return;
        }

        channame=getoken(client->msg,3,' ');

        chan=get_chan(*ppchans,channame);
        if (chan == NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Tens que entrar primeiro no canal\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                free(channame);
                return;
        }

        if (chan->reg != NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :O canal %s ja' esta' registado\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,channame);
                free(channame);
                return;
        }

        user=get_user_in_chan(chan,client->nick);
        if (user == NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Tens que entrar primeiro no canal\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                free(channame);
                return;
        }

        if (compic(channame,"#unregisted") || compic(channame,"#unidentified"))
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Canal %s não pode ser registado!\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,channame);
                free(channame);
                return;
        }

        if (get_quant_user_in_chan(chan) != 1)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :So' podes estar tu no canal\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                free(channame);
                return;
        }

        password=getoken(client->msg,4,' ');

        if (strlen(password) > PASSWORD_LEN_LIMIT)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :A password não pode ter mais de %d caracteres\r\n",
                          CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,PASSWORD_LEN_LIMIT);
                free(password);
                free(channame);
                return;
        }

        if (strlen(password) < PASSWORD_MIN_LEN)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :A password não pode ter menos de %d caracteres\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,PASSWORD_MIN_LEN);
                free(password);
                free(channame);
                return;
        }

        chan->reg=malloc(sizeof(*(chan->reg)));
        memerror(chan->reg);
        /*chanserv register #chan pass desc*/

        while (client->msg[counter] != '\0')
        {
                if (client->msg[counter] == ' ') s_counter++;
                if (s_counter == 4) {
                        px=counter+1;
                        break;
                }
                counter++;
        }

        chan->reg->desc=malloc(strlen(&client->msg[px])+1);
        memerror(chan->reg->desc);
        strcpy(chan->reg->desc,&client->msg[px]);

        chan->reg->founder=malloc(strlen(client->nick)+1);
        memerror(chan->reg->founder);
        strcpy(chan->reg->founder,client->nick);

        chan->reg->password=password;

        chan->reg->email=NULL;
        chan->reg->url=NULL;

        chan->reg->akicks=NULL;

        chan->reg->ausers=NULL;

        alloc_auser_list(&chan->reg->ausers);

        chan->reg->ausers->level=15;
        chan->reg->ausers->num=1;
        chan->reg->ausers->founder_access=false;
        chan->reg->ausers->nick=malloc(strlen(client->nick)+1);
        memerror(chan->reg->ausers->nick);
        strcpy(chan->reg->ausers->nick,client->nick);

        chan->reg->set.priv=false;
        chan->reg->set.mode_lock=false;
        chan->reg->set.topic_lock=false;
        chan->reg->set.secure_ops=false;
        chan->reg->set.restricted=false;

        time(&chan->reg->reg_time);
        chan->reg->last_use=chan->reg->reg_time;

        chan->reg->users_record=1;
        chan->reg->record_time=chan->reg->reg_time;

        chan->reg->levels.autoop=CHANSERV_LEVEL_AUTOOP;
        chan->reg->levels.autovoice=CHANSERV_LEVEL_AUTOVOICE;
        chan->reg->levels.autodeop=CHANSERV_LEVEL_AUTODEOP;
        chan->reg->levels.invite=CHANSERV_LEVEL_INVITE;
        chan->reg->levels.akick=CHANSERV_LEVEL_AKICK;
        chan->reg->levels.set=CHANSERV_LEVEL_SET;
        chan->reg->levels.clear=CHANSERV_LEVEL_CLEAR;
        chan->reg->levels.unban=CHANSERV_LEVEL_UNBAN;
        chan->reg->levels.opdeop=CHANSERV_LEVEL_OPDEOP;
        chan->reg->levels.kick=CHANSERV_LEVEL_KICK;
        chan->reg->levels.nojoin=CHANSERV_LEVEL_NOJOIN;
        chan->reg->levels.access_list=CHANSERV_LEVEL_ACCESS_LIST;
        chan->reg->levels.access_change=CHANSERV_LEVEL_ACCESS_CHANGE;
        chan->reg->levels.memo_read=CHANSERV_LEVEL_MEMO_READ;
        chan->reg->levels.memo_send=CHANSERV_LEVEL_MEMO_SEND;
        chan->reg->levels.memo_del=CHANSERV_LEVEL_MEMO_DEL;
        chan->reg->never_unreg=client->ircop;

        save_chanreg(chan);

        write_sock(client,":%s!%s@%s NOTICE %s :Canal %s registado com o teu nick (%s) como founder.\r\n",
                                CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,channame,client->nick);
        write_sock(client,":%s!%s@%s NOTICE %s :Não esqueças a password do canal!"
                          " Lembra-te que ela e' case sensitive (\"A\" diferente de \"a\")."
                          " Se o canal não for usado por %d dias ele sera' desregistado!\r\n",
                                CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,REMOVE_CHAN_TIMEOUT);

        free(channame);
}

static void chanserv_identify(CLIENTS *client, CHANS **ppchans)
{
        CHANS *pchans;
        char *chan;
        char *pw;

        if (tknum(client->msg,' ') == 2) {
                show_chanserv_help(client,HELP_IDENTIFY);
                return;
        }

        if (tknum(client->msg,' ') < 4) {
                write_sock(client,":%s!%s@%s NOTICE %s :Identify: Poucos parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                return;
        }

        if (tknum(client->msg,' ') > 4) {
                write_sock(client,":%s!%s@%s NOTICE %s :Identify: Demaseados parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                return;
        }
        /*chanserv identify #chan password*/

        chan=getoken(client->msg,3,' ');
        pchans=get_chan(*ppchans,chan);

        if (pchans == NULL || pchans->reg == NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Canal não registado\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                free(chan);
                return;
        }

        free(chan);

        if (get_user_access_level(pchans->reg->ausers,client->nick) != 15) {
                write_sock(client,":%s!%s@%s NOTICE %s :Acesso negado!\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                return;
        }

        pw=getoken(client->msg,4,' ');

        if (comp(pw,pchans->reg->password))
        {
                struct access_users *auser;
                auser=get_user_access(pchans->reg->ausers,client->nick);
                auser->founder_access=true;
                write_sock(client,":%s!%s@%s NOTICE %s :Password aceite - Tem agora acesso com nivel founder a %s.\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,pchans->name);
        }
        else
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Password incorrecta\r\n",
                                CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                free(pw);
                client->ident_failed++;
                return;
        }
        free(pw);
}

static void chanserv_info(CLIENTS *client, CHANS **ppchans)
{
        CHANS *tchan;
        char *chan;
        char *mes[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

        if (tknum(client->msg,' ') == 2) {
                show_chanserv_help(client,HELP_INFO);
                return;
        }

        if (tknum(client->msg,' ') > 3) {
                write_sock(client,":%s!%s@%s NOTICE %s :Info: Demaseados parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                return;
        }

        chan=getoken(client->msg,3,' ');

        tchan=get_chan(*ppchans,chan);

        if (tchan == NULL || tchan->reg == NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Canal %s não registado\r\n",
                                CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,chan);
                free(chan);
                return;
        }

        if (tchan->reg->set.priv && !client->ircop && (get_user_access_level(tchan->reg->ausers,client->nick) < 1
            || client->reg == NULL || (client->reg == NULL && client->reg->status != USER_STATUS_IDENTIFIED)))
                /*se o canal for privado e o requester não for ircop e (o cliente nao tiver acesso ou nao tiver registado ou nao tiver
                                                                                                            identificado): mostra privado*/
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Informação do canal %s:\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tchan->name);
                write_sock(client,":%s!%s@%s NOTICE %s :Founder: %s\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tchan->reg->founder);
        }
        else
        {
                struct tm *tminfo;
                time_t tm_cpy;
                time_t now;
                long ltmp;

                write_sock(client,":%s!%s@%s NOTICE %s :Informação do canal %s:\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tchan->name);
                write_sock(client,":%s!%s@%s NOTICE %s :Founder: %s\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tchan->reg->founder);
                write_sock(client,":%s!%s@%s NOTICE %s :Descrição: %s\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tchan->reg->desc);

                time(&now);

                tm_cpy=tchan->reg->reg_time;
                tminfo=localtime(&tm_cpy);

                ltmp=(long)(now-tchan->reg->reg_time);

                write_sock(client,":%s!%s@%s NOTICE %s :Registado: %s %d %02d:%02d:%02d %d"
                                  " [Ha' %d dias, %d horas e %d mins]\r\n",
                                  CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,mes[tminfo->tm_mon],tminfo->tm_mday,
                                  tminfo->tm_hour,tminfo->tm_min,tminfo->tm_sec,tminfo->tm_year+1900,
                                  ltmp/60/60/24,ltmp/60/60%24,ltmp/60%60);

                if (tchan->users != NULL) /*Ha' gente no canal*/
                {
                        tm_cpy=now;
                        tminfo=localtime(&tm_cpy);

                        ltmp=(long)now;

                        write_sock(client,":%s!%s@%s NOTICE %s :Ultima utilização: %s %d %02d:%02d:%02d %d\r\n",
                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,mes[tminfo->tm_mon],tminfo->tm_mday,
                                        tminfo->tm_hour,tminfo->tm_min,tminfo->tm_sec,tminfo->tm_year+1900);
                }
                else
                {
                        tm_cpy=tchan->reg->last_use;
                        tminfo=localtime(&tm_cpy);

                        ltmp=(long)(now-tchan->reg->last_use);

                        write_sock(client,":%s!%s@%s NOTICE %s :Ultima utilização: %s %d %02d:%02d:%02d %d\r\n"
                                          " [Ha' %d dias, %d horas e %d mins]\r\n",
                                          CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,mes[tminfo->tm_mon],tminfo->tm_mday,
                                          tminfo->tm_hour,tminfo->tm_min,tminfo->tm_sec,tminfo->tm_year+1900,
                                          ltmp/60/60/24,ltmp/60/60%24,ltmp/60%60);
                }

                tm_cpy=tchan->reg->record_time;
                tminfo=localtime(&tm_cpy);

                ltmp=(long)(now-tchan->reg->record_time);

                write_sock(client,":%s!%s@%s NOTICE %s :Record de users: %ld (%s %d %02d:%02d:%02d %d"
                                  " [Ha' %d dias, %d horas e %d mins])\r\n",
                                  CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tchan->reg->users_record,mes[tminfo->tm_mon],
                                  tminfo->tm_mday,tminfo->tm_hour,tminfo->tm_min,tminfo->tm_sec,tminfo->tm_year+1900,
                                  ltmp/60/60/24,ltmp/60/60%24,ltmp/60%60);


                if (tchan->topic != NULL)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Tópico: %s\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tchan->topic);
                        write_sock(client,":%s!%s@%s NOTICE %s :autor: %s\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tchan->topic_set_by);
                }
                if (tchan->reg->set.priv || tchan->reg->set.mode_lock || tchan->reg->set.topic_lock || tchan->reg->set.secure_ops
                    || tchan->reg->set.restricted)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Opções: %s%s%s%s%s\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,
                                   (tchan->reg->set.priv) ? "Private": "",(tchan->reg->set.topic_lock) ? ", Topic Lock": "",
                                   (tchan->reg->set.mode_lock) ? ", Mode Lock": "",(tchan->reg->set.secure_ops) ? ", Secure Ops": "",
                                   (tchan->reg->set.restricted) ? ", Restricted": "");
                }

                if (tchan->reg->url != NULL)
                        write_sock(client,":%s!%s@%s NOTICE %s :URL: %s\r\n",
                                CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tchan->reg->url);

                if (tchan->reg->email != NULL)
                        write_sock(client,":%s!%s@%s NOTICE %s :E-mail: %s\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tchan->reg->email);

                if (tchan->reg->email != NULL)
                        write_sock(client,":%s!%s@%s NOTICE %s :E-mail: %s\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tchan->reg->email);

                if (tchan->users != NULL)
                        write_sock(client,":%s!%s@%s NOTICE %s :Activo: %ld utilizadores\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,get_quant_user_in_chan(tchan));
                /*
                Informacao do canal %s:
                Founder: %s
                Descrição: %s
                Registado: Jun 30 14:03:43 2003
                Ultima utilização: Nov 13 21:04:26 2003
                Record de users: rec (Nov 13 21:04:26 2003)
                Tópico: topico
                autor: orium
                Opções: Secure Ops, Restricted
                URL: %s
                E-mail: %s
                Activo: 5 utilizadores
                */
        }

        free(chan);
}

static void chanserv_access(CLIENTS *client, CHANS **ppchans, USERREG *user_regs)
{
        char *cmd;
        char *chan_name;
        CHANS *chan;

        if (tknum(client->msg,' ') == 2) {
                show_chanserv_help(client,HELP_ACCESS);
                return;
        }

        if (tknum(client->msg,' ') < 4) {
                write_sock(client,":%s!%s@%s NOTICE %s :Access: Poucos parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                return;
        }

        chan_name=getoken(client->msg,3,' ');

        chan=get_chan(*ppchans,chan_name);

        if (chan == NULL || chan->reg == NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Canal %s não registado\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,chan_name);
                free(chan_name);
                return;
        }

        cmd=getoken(client->msg,4,' ');
        if (compic("list",cmd))
        {/*chanserv access #chan list*/
                struct access_users *ausers;

                if (tknum(client->msg,' ') > 4) {
                        write_sock(client,":%s!%s@%s NOTICE %s :Access list: Demaseados parametros\r\n",
                                          CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        return;
                }

                if (get_user_access_level(chan->reg->ausers,client->nick) < chan->reg->levels.access_list && !client->ircop)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Acesso negado!\r\n",
                                          CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        return;
                }
                ausers=chan->reg->ausers;

                write_sock(client,":%s!%s@%s NOTICE %s :LISTA DE ACESSO A %s:\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,chan->name);
                write_sock(client,":%s!%s@%s NOTICE %s :Num Niv Nick\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,chan->name);

                while (ausers != NULL)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :%d %d %s\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,ausers->num,(int)ausers->level,ausers->nick);
                        ausers=ausers->next;
                }

        }
        else if (compic("add",cmd))
        {/*chanserv access #chan add nick nivel*/
                veryshort access_level;
                veryshort tlevel;
                char *tnick;
                char *tmp;
                struct access_users *ausers;

                if (tknum(client->msg,' ') > 6)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Access add: Demaseados parametros\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        return;
                }

                if (tknum(client->msg,' ') < 6)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Access add: Poucos parametros\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        return;
                }

                access_level=get_user_access_level(chan->reg->ausers,client->nick);

                if (access_level < chan->reg->levels.access_change && !client->ircop)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Acesso negado!\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        return;
                }

                tmp=getoken(client->msg,6,' ');
                if (!isstrnum(tmp))
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Nivel inválido\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        free(tmp);
                        return;
                }

                tlevel=(veryshort)atoi(tmp);
                free(tmp);

                if (tlevel > 15 || tlevel < -5)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Nivel inválido\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        return;
                }

                if ((tlevel >= access_level || access_level < 0) && !client->ircop)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Acesso negado!\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        return;
                }

                tnick=getoken(client->msg,5,' ');
                if (get_raw_user_reg(user_regs,tnick) == NULL)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Nick %s não esta' registado\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick);
                        free(cmd);
                        free(chan_name);
                        free(tnick);
                        return;
                }

                if (get_user_access_level(chan->reg->ausers,tnick) >= access_level && !client->ircop)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Acesso negado!\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        free(tnick);
                        return;
                }

                if (compic(tnick,chan->reg->founder))
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :O nivel do founder nunca pode ser alterado\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        free(tnick);
                        return;
                }

                ausers=get_user_access(chan->reg->ausers,tnick);

                if (tlevel == 0)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Nivel de acesso 0 implica remover o user da lista de acesso.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        write_sock(client,":%s!%s@%s NOTICE %s :Se e' isto que queres escreve: /%s access %s del %s\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME,chan->name,tnick);
                        free(cmd);
                        free(chan_name);
                        free(tnick);
                        return;
                }

                if (ausers != NULL) /*ja' existe*/
                {
                        if (ausers->level == tlevel)
                                write_sock(client,":%s!%s@%s NOTICE %s :Nivel de acesso de %s em %s inalterado de %d.\r\n",
                                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan->name,(int)tlevel);
                        else
                        {
                                ausers->level=tlevel;
                                write_sock(client,":%s!%s@%s NOTICE %s :Nivel de acesso de %s em %s alterado para %d.\r\n",
                                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan->name,(int)tlevel);
                        }

                        free(cmd);
                        free(chan_name);
                        free(tnick);
                        return;
                }
                else
                {
                        if (!client->ircop && get_quant_ausers_of_chan(chan->reg->ausers) >= MAX_AUSERS)
                        {
                                write_sock(client,":%s!%s@%s NOTICE %s :Atingido limite da lista de acesso de %s\r\n",
                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,chan->name);
                                free(cmd);
                                free(chan_name);
                                free(tnick);
                        }

                        ausers=alloc_auser_list(&chan->reg->ausers);

                        ausers->nick=tnick;
                        ausers->level=tlevel;
                        ausers->num=get_auser_max_num(chan->reg->ausers)+1;
                        ausers->founder_access=false;

                        write_sock(client,":%s!%s@%s NOTICE %s :%s adicionado a' lista de acesso de %s com nivel %d.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan->name,(int)tlevel);
                }
        }
        else if (compic("del",cmd))
        {/*chanserv access #chan del nick*/
                char *tnick;
                veryshort access_level;
                struct access_users *ausers;

                if (tknum(client->msg,' ') > 5)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Access del: Demaseados parametros\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        return;
                }

                if (tknum(client->msg,' ') < 5)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Access del: Poucos parametros\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        return;
                }

                access_level=get_user_access_level(chan->reg->ausers,client->nick);

                if (access_level < chan->reg->levels.access_change && !client->ircop)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Acesso negado!\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        return;
                }

                tnick=getoken(client->msg,5,' ');

                if (get_user_access_level(chan->reg->ausers,tnick) >= access_level && !client->ircop)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Acesso negado!\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        free(tnick);
                        return;
                }

                if (compic(tnick,chan->reg->founder))
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :O founder não pode ser apagado da lista de acesso\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                        free(cmd);
                        free(chan_name);
                        free(tnick);
                        return;
                }

                ausers=get_user_access(chan->reg->ausers,tnick);

                if (ausers == NULL)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :%s não esta' na lista de acesso a %s.\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan->name);
                        free(cmd);
                        free(chan_name);
                        free(tnick);
                        return;
                }

                rm_auser_list(&chan->reg->ausers,ausers);

                free(ausers->nick);
                free(ausers);

                write_sock(client,":%s!%s@%s NOTICE %s :%s removido da lista de acesso a %s.\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan->name);

                free(tnick);
        }
        else
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Comando access %s invalido\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,cmd);
                write_sock(client,":%s!%s@%s NOTICE %s :Para mais informações escreve: /%s help access\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,CHANSERV_NAME);
                free(cmd);
                free(chan_name);
                return;
        }

        save_chanreg(chan);

        free(cmd);
        free(chan_name);
}

static void chanserv_opdeop(CLIENTS *client, CHANS **ppchans, USERREG *user_regs, bool op)
{
        char *chan_name;
        char *tnick;
        CHANS *chan;
        veryshort rlevel;
        bool inic_op_status;
        struct chan_users_list *tu_c;

        if (tknum(client->msg,' ') == 2)
        {
                show_chanserv_help(client,(op) ? HELP_OP : HELP_DEOP);
                return;
        }

        if (tknum(client->msg,' ') < 3)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :%s: Poucos parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,(op) ? "Op" : "Deop");
                return;
        }

        if (tknum(client->msg,' ') > 4)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :%s: Demaseados parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,(op) ? "Op" : "Deop");
                return;
        }

        chan_name=getoken(client->msg,3,' ');

        chan=get_chan(*ppchans,chan_name);

        if (chan == NULL || chan->reg == NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Canal %s não registado\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,chan_name);
                free(chan_name);
                return;
        }

        if ((rlevel=get_user_access_level(chan->reg->ausers,client->nick)) < chan->reg->levels.opdeop && !client->ircop)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Acesso negado!\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                free(chan_name);
                return;
        }
        /*chanserv op/deop <#chan> [nick]*/
        if (tknum(client->msg,' ') != 3)
        {
                tnick=getoken(client->msg,4,' ');

                if (!compic(tnick,client->nick))
                {
                        if (op)
                        {
                                if (get_user_access_level(chan->reg->ausers,tnick) <= chan->reg->levels.autodeop && !client->ircop)
                                {
                                        write_sock(client,":%s!%s@%s NOTICE %s :"
                                                        "O nivel de acesso do user %s ao canal %s não te permite ter op\r\n",
                                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan->name);
                                        free(chan_name);
                                        free(tnick);
                                        return;
                                }
                        }
                        else
                        {
                                /*não pode deopar users com acesso superior ou igual ao dele*/
                                if (rlevel >= get_user_access_level(chan->reg->ausers,tnick) && !client->ircop)
                                {
                                        write_sock(client,":%s!%s@%s NOTICE %s :"
                                                        "O nivel de acesso do user %s ao canal %s não te permite ter op\r\n",
                                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan->name);
                                        free(chan_name);
                                        free(tnick);
                                        return;
                                }
                        }

                        if ((tu_c=get_user_in_chan(chan,tnick)) == NULL)
                        {
                                write_sock(client,":%s!%s@%s NOTICE %s :O user %s não esta no' canal %s\r\n",
                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan->name);
                                free(chan_name);
                                free(tnick);
                                return;
                        }

                        if (op)
                        {
                                if (tu_c->user->reg == NULL)
                                {
                                        write_sock(client,":%s!%s@%s NOTICE %s :O user %s não esta' registado\r\n",
                                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick);
                                        free(chan_name);
                                        free(tnick);
                                        return;
                                }
                                if (tu_c->user->reg->status != USER_STATUS_IDENTIFIED)
                                {
                                        write_sock(client,":%s!%s@%s NOTICE %s :O user %s não esta' identificado\r\n",
                                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick);
                                        free(chan_name);
                                        free(tnick);
                                        return;
                                }
                        }

                        inic_op_status=tu_c->op;
                        tu_c->op=(op) ? true : false;
                        get_chan_from_user_list(tu_c->user->chans,chan_name)->op=(op) ? true : false;
                }
                else
                {
                        tu_c=get_user_in_chan(chan,tnick);
                        inic_op_status=tu_c->op;
                        tu_c->op=(op) ? true : false;
                        get_chan_from_user_list(client->chans,chan_name)->op=(op) ? true : false;
                }
        }
        else
        {
                tu_c=get_user_in_chan(chan,client->nick);
                inic_op_status=tu_c->op;
                tu_c->op=(op) ? true : false;
                get_chan_from_user_list(client->chans,chan_name)->op=(op) ? true : false;

                tnick=malloc(strlen(client->nick)+1);
                memerror(tnick);
                strcpy(tnick,client->nick);
        }

        if (op && inic_op_status) /*se e' para por op e o user ja tem op*/
        {
                if (tknum(client->msg,' ') != 3)
                        free(tnick);
                free(chan_name);
                return;
        }

        if (!op && !inic_op_status) /*se e' para tirar op e o user não tem op*/
        {
                if (tknum(client->msg,' ') != 3)
                        free(tnick);
                free(chan_name);
                return;
        }

        write_sock(client,":%s!%s@%s MODE %s %co %s\r\n",
                   CHANSERV_NAME,USER_NAME,SERVER_NAME,chan->name,
                   (op) ? '+' : '-',tnick);

        send_to_all_in_chan(client,chan_name,
                            ":%s!%s@%s MODE %s %co %s\r\n",
                            CHANSERV_NAME,USER_NAME,SERVER_NAME,
                            chan->name,
                            (op) ? '+' : '-',tnick);

        free(tnick);
        free(chan_name);
}

static void chanserv_invite(CLIENTS *client, CHANS **ppchans, USERREG *user_regs)
{
        char *chan_name;
        char *tnick;
        CHANS *chan;
        USERREG *target;
        /*chanserv invite #chan nick*/

        if (tknum(client->msg,' ') == 2) {
                show_chanserv_help(client,HELP_INVITE);
                return;
        }

        if (tknum(client->msg,' ') < 3) {
                write_sock(client,":%s!%s@%s NOTICE %s :Invite: Poucos parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                return;
        }

        if (tknum(client->msg,' ') > 4) {
                write_sock(client,":%s!%s@%s NOTICE %s :Invite: Demaseados parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                return;
        }

        chan_name=getoken(client->msg,3,' ');

        chan=get_chan(*ppchans,chan_name);

        if (chan == NULL || chan->reg == NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Canal %s não registado\r\n",
                                  CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,chan_name);
                free(chan_name);
                return;
        }

        if (get_user_access_level(chan->reg->ausers,client->nick) < chan->reg->levels.invite && !client->ircop)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Acesso negado!\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                free(chan_name);
                return;
        }

        tnick=getoken(client->msg,4,' ');

        if (get_user_in_chan(chan,tnick) != NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :O utilizador %s ja' esta' no canal %s\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan_name);
                free(chan_name);
                free(tnick);
                return;
        }

        target=get_raw_user_reg(user_regs,tnick);

        if (target == NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :O utilizador %s não esta' registado\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick);
                free(chan_name);
                free(tnick);
                return;
        }

        if (target->status == USER_STATUS_OFFLINE)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :O utilizador %s não esta' online\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick);
                free(chan_name);
                free(tnick);
                return;
        }

        if (target->status != USER_STATUS_IDENTIFIED)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :O utilizador %s não esta' identificado\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick);
                free(chan_name);
                free(tnick);
                return;
        }

        write_sock(target->user,":%s!%s@%s INVITE %s :%s\r\n",CHANSERV_NAME,USER_NAME,SERVER_NAME,tnick,chan->name);

        free(chan_name);
        free(tnick);
}

static void chanserv_kick(CLIENTS *client,CHANS **ppchans, CLIENTS **clients, long *channels)
{
        char *chan_name;
        char *tnick;
        char *msg;
        CHANS *chan;
        CLIENTS *target;
        veryshort rlevel;

        if (tknum(client->msg,' ') == 2) {
                show_chanserv_help(client,HELP_KICK);
                return;
        }

        if (tknum(client->msg,' ') < 4) {
                write_sock(client,":%s!%s@%s NOTICE %s :Kick: Poucos parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                return;
        }

        chan_name=getoken(client->msg,3,' ');

        chan=get_chan(*ppchans,chan_name);

        if (chan == NULL || chan->reg == NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Canal %s não registado\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,chan_name);
                free(chan_name);
                return;
        }

        if ((rlevel=get_user_access_level(chan->reg->ausers,client->nick)) < chan->reg->levels.kick && !client->ircop)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Acesso negado!\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                free(chan_name);
                return;
        }

        tnick=getoken(client->msg,4,' ');

        if (get_user_in_chan(chan,tnick) == NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :O utilizador %s não esta' no canal %s\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan_name);
                free(chan_name);
                free(tnick);
                return;
        }
        /*chanserv kick #chan nick [motivo]*/
        //alvo nao pode ser ircop

        if (get_user_access_level(chan->reg->ausers,tnick) > rlevel && !client->ircop)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :O nivel de acesso do user %s ao canal %s não te permite kicka-lo\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan->name);

                free(chan_name);
                free(tnick);
                return;
        }

        target=get_client(*clients,tnick);

        if (target->ircop)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Não podes kickar um ircop\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);

                free(chan_name);
                free(tnick);
                return;
        }
        /*...*/

        if (tknum(client->msg,' ') == 4)
        {
                /*:orium!~orium@irc.ptsida.org KICK #hehe espectador :espectador*/
                write_sock(target,":%s!%s@%s KICK %s %s :%s\r\n",CHANSERV_NAME,USER_NAME,SERVER_NAME,chan->name,target->nick,
                                                                     target->nick);
                send_to_all_in_chan(target,chan_name,":%s!%s@%s KICK %s %s :%s\r\n",CHANSERV_NAME,USER_NAME,SERVER_NAME,chan->name,
                                                                                   target->nick,target->nick);
        }
        else
        {
                char *kmsg;

                kmsg=get_irc_str(client->msg,5);
                if (kmsg == NULL)
                {
                        free(chan_name);
                        free(tnick);
                        return;
                }

                write_sock(target,":%s!%s@%s KICK %s %s :%s\r\n",CHANSERV_NAME,USER_NAME,SERVER_NAME,chan->name,target->nick,kmsg);
                send_to_all_in_chan(target,chan_name,":%s!%s@%s KICK %s %s :%s\r\n",CHANSERV_NAME,USER_NAME,SERVER_NAME,chan->name,
                                                                                   target->nick,kmsg);

                free(kmsg);
        }

        msg=target->msg;

        target->msg=malloc(strlen(chan_name)+6); /*PART #CHAN*/

        sprintf(target->msg,"PART %s",chan_name);

        part(target,ppchans,true,channels,false);

        target->msg=msg;

        free(chan_name);
        free(tnick);
}

static void chanserv_unban(CLIENTS *client, CHANS *chans)
{
        CHANS *chan;
        char *chan_name;
        char *ban_mask;

        if (tknum(client->msg,' ') == 2) {
                show_chanserv_help(client,HELP_UNBAN);
                return;
        }

        if (tknum(client->msg,' ') < 3) {
                write_sock(client,":%s!%s@%s NOTICE %s :Unban: Poucos parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                return;
        }

        if (tknum(client->msg,' ') > 4) {
                write_sock(client,":%s!%s@%s NOTICE %s :Unban: Demaseados parametros\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                return;
        }
        /*chanserv unban #chan [mask]*/

        chan_name=getoken(client->msg,3,' ');

        chan=get_chan(chans,chan_name);

        if (chan == NULL || chan->reg == NULL)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Canal %s não registado\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,chan_name);
                free(chan_name);
                return;
        }

        if (get_user_access_level(chan->reg->ausers,client->nick) < chan->reg->levels.unban)
        {
                write_sock(client,":%s!%s@%s NOTICE %s :Acesso negado!\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                free(chan_name);
                return;
        }

        ban_mask=getoken(client->msg,4,' ');
        if (ban_mask != NULL)
        {
                unset_ban(client,chan,ban_mask,chan_name,true);
                free(ban_mask);
        }
        else
        {
                char *tmp;

                unset_ban(client,chan,client->nick,chan_name,true);

                tmp=malloc(strlen(client->md5_ip)+5);

                sprintf(tmp,"*!*@%s",client->md5_ip);

                unset_ban(client,chan,tmp,chan_name,true);

                free(tmp);
        }

        free(chan_name);
}
/*
SET

- ChanServ - Uso: set #canal <opção> <parametros>
- ChanServ - Este comando permite, ao fundador do canal, configurar algumas
- ChanServ - opcoes. O founder de canal tera de se identificar com o comando
- ChanServ - IDENTIFY antes de usar o comandos que nessessitam de acesso founder.
- ChanServ - Opções disponiveis:
- ChanServ - FOUNDER Define o Fundador do canal
- ChanServ - PASSWORD Altera a password do Fundador do canal
- ChanServ - DESC Altera a descricao do canal
- ChanServ - URL Associa um endereco URL ao canal
- ChanServ - EMAIL Associa um endereco E-mail ao canal
- ChanServ - TOPIC Altera o topico do canal
- ChanServ - MLOCK Bloqueia/desbloqueia modos do canal
- ChanServ - TOPICLOCK O topico so podera ser alterado com o comando SET TOPIC
- ChanServ - PRIVATE Oculta o canal da lista do ChanServ
- ChanServ - SECUREOPS Restringue o acesso a operadores do canal
- ChanServ - RESTRICTED Restringue o acesso ao canal
- ChanServ - SECURE Activa os metodos seguros do ChanServ
- ChanServ - Execute /chanserv help set <opção> para mais informações sobre
- ChanServ - uma opção em particular.
*/
/*
Apagar Canal: chanserv drop #
Unban nick:/chanserv unban # $$?="Nick ?"
-
.Levels
..Reset levels:/chanserv levels # reset
..List levels sets:/chanserv levels # list
.-
.Akick
..Lista de Akick: chanserv akick # list
..-
..Adicionar Akick: chanserv akick # add $$?="Nick" $$?="Razão?"
..Apagar Akick: chanserv akick # del $$?="Nick ?"
.PréDefenidos
..AutoOp
...Nível: chanserv levels # SET AUTOOP $$?"Nível (-1)"
...Desativar: chanserv LEVELS # DIS AUTOOP
..AutoVoice
...Nível: chanserv LEVELS # SET AUTOVOICE $$?"Nível (-1)"
...Desativar: chanserv LEVELS # DIS AUTOVOICE
..AutoDeop
...Nível: chanserv LEVELS # SET AUTODEOP $$?"Nível "
...Desativar: chanserv LEVELS # DIS AUTODEOP
..NoJoin
...Nível: chanserv LEVELS # SET NOJOIN $$?"Nível "
...Desativar: chanserv LEVELS # DIS NOJOIN
..Invite
...Nível: chanserv LEVELS # SET INVITE $$?"Nível "
...Desativar: chanserv LEVELS # DIS INVITE
..Akick
...Nível: chanserv LEVELS # SET AKICK $$?"Nível "
...Desativar: chanserv LEVELS # DIS AKICK
..SET
...Nível: chanserv LEVELS # SET SET $$?"Nível "
...Desativar: chanserv LEVELS # DIS SET
..Clear
...Nível: chanserv LEVELS # SET CLEAR $$?"Nível "
...Desativar: chanserv LEVELS # DIS CLEAR
..Unban
...Nível: chanserv LEVELS # SET UNBAN $$?"Nível "
...Desativar: chanserv LEVELS # DIS UNBAN
..OpDeop
...Nível: chanserv LEVELS # SET OPDEOP $$?"Nível "
...Desativar: chanserv LEVELS # DIS OPDEOP...ACC-List....Nível: chanserv LEVELS # SET ACC-LIST $$?"Nível "
...Desativar: chanserv LEVELS # DIS ACC-LIST
..ACC-Change
...Nível: chanserv LEVELS # SET ACC-CHANGE $$?"Nível "
...Desativar: chanserv LEVELS # DIS ACC-CHANGE
..Memo
...Read
....Nível: chanserv LEVELS # SET MEMO_READ $$?"Nível "
....Desativar: chanserv LEVELS # DIS MEMO_READ
...Send
....Nível: chanserv LEVELS # SET MEMO_SEND $$?"Nível "
....Desativar: chanserv LEVELS # DIS MEMO_SEND
...Del
....Nível: chanserv LEVELS # SET MEMO_DEL $$?"Nível "
....Desativar: chanserv LEVELS # DIS MEMO_DEL
...-
...Resetar todos os Niveis: chanserv LEVELS # RESET
.Definir
..Novo Founder: chanserv set # founder $$?="Nick do novo founder"
..Alterar Password: chanserv set # password $$?="Nova Pass?"
..-
..Nova descrição: chanserv set # desc $$?="Descrição?"
..Definir Pagina De Web: chanserv set # set url $$?="Página Da Net??  (morada)"
..Definir E-mail: chanserv set # set email $$?="E-mail??"
..Novo Tópico: chanserv set # topic $$?="Novo tópico?"
..-
..Set Topiclock
...On: chanserv set # topiclock on
...Off: chanserv set # topiclock off
..Set Private
...On: chanserv set # private on
...Off: chanserv set # private off
..Set Mlock
... chanserv set # mlock on
... chanserv set # mlock off
*secureops
*restricted
.Clear
..Clear Ops: chanserv clear # ops
..Clear Voices: chanserv clear # voices
..Clear Modes: chanserv clear # modes
..Clear Bans: chanserv clear # bans
..Clear Users: chanserv clear # users
*/
/*
Drop
Akick
Set
2 clear
1 banlist (so' se não estiver no canal) (so para quem tem acesso ao unban)
3 userlist (so' se não tiver no canal) (so para quem tem acesso ao kick)


set banlock on

forcar CHANSERV_LEVEL_ACCESS_CHANGE > 0

por cena para gravar e carregar
*/
void chanserv(CLIENTS *client, CHANS **ppchans, USERREG *user_regs, CLIENTS **clients, long *channels)
{
        char *command;

        if (tknum(client->msg,' ') == 1) {
                show_chanserv_help(client,HELP_MAIN);
                return;
        }

        command=getoken(client->msg,2,' ');

        if (compic(command,"help"))
        {
                char *cmd2;

                if (tknum(client->msg,' ') == 2) {
                        show_chanserv_help(client,HELP_MAIN);
                        free(command);
                        return;
                }

                cmd2=getoken(client->msg,3,' ');
                if (tknum(client->msg,' ') == 3)
                {
                        if (compic(cmd2,"register"))
                        {
                                show_chanserv_help(client,HELP_REGISTER);
                                free(command);
                                free(cmd2);
                                return;
                        }
                        if (compic(cmd2,"identify"))
                        {
                                show_chanserv_help(client,HELP_IDENTIFY);
                                free(command);
                                free(cmd2);
                                return;
                        }
                        if (compic(cmd2,"info"))
                        {
                                show_chanserv_help(client,HELP_INFO);
                                free(command);
                                free(cmd2);
                                return;
                        }
                        if (compic(cmd2,"access"))
                        {
                                show_chanserv_help(client,HELP_INFO);
                                free(command);
                                free(cmd2);
                                return;
                        }
                        if (compic(cmd2,"op"))
                        {
                                show_chanserv_help(client,HELP_OP);
                                free(command);
                                free(cmd2);
                                return;
                        }
                        if (compic(cmd2,"deop"))
                        {
                                show_chanserv_help(client,HELP_DEOP);
                                free(command);
                                free(cmd2);
                                return;
                        }
                        if (compic(cmd2,"invite"))
                        {
                                show_chanserv_help(client,HELP_INVITE);
                                free(command);
                                free(cmd2);
                                return;
                        }
                        if (compic(cmd2,"kick"))
                        {
                                show_chanserv_help(client,HELP_KICK);
                                free(command);
                                free(cmd2);
                                return;
                        }
                        if (compic(cmd2,"unban"))
                        {
                                show_chanserv_help(client,HELP_UNBAN);
                                free(command);
                                free(cmd2);
                                return;
                        }
                }

                if (tknum(client->msg,' ') >= 4)
                {
                        if (compic(cmd2,"access"))
                        {
                                char *cmd3;
                                cmd3=getoken(client->msg,4,' ');
                                if (compic(cmd3,"add"))
                                {
                                        show_chanserv_help(client,HELP_ACCESS_ADD);
                                        free(command);
                                        free(cmd2);
                                        free(cmd3);
                                        return;
                                }
                                if (compic(cmd3,"del"))
                                {
                                        show_chanserv_help(client,HELP_ACCESS_DEL);
                                        free(command);
                                        free(cmd2);
                                        free(cmd3);
                                        return;
                                }
                                if (compic(cmd3,"list"))
                                {
                                        show_chanserv_help(client,HELP_ACCESS_LIST);
                                        free(command);
                                        free(cmd2);
                                        free(cmd3);
                                        return;
                                }
                                show_chanserv_help(client,HELP_UNKNOWN_ACCESS_COMMAND);
                                free(command);
                                free(cmd2);
                                free(cmd3);
                                return;
                        }
                }

                show_chanserv_help(client,HELP_UNKNOWN_COMMAND);
                free(command);
                free(cmd2);
                return;
        }

        if (chanserv_iscommand(command))
        {
                if (compic(command,"info"))
                {
                        chanserv_info(client,ppchans);
                        free(command);
                        return;
                }

                if (client->reg == NULL)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Para usares o chanserv tens de estar registado!\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,client->nick);
                        free(command);
                        return;
                }

                if (client->reg->status != USER_STATUS_IDENTIFIED)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :Para usares o chanserv tens de estar identificado!\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,client->nick);
                        free(command);
                        return;
                }

                if (compic(command,"register")) chanserv_register(client,ppchans);
                if (compic(command,"identify")) chanserv_identify(client,ppchans);
                if (compic(command,"access")) chanserv_access(client,ppchans,user_regs);
                if (compic(command,"op")) chanserv_opdeop(client,ppchans,user_regs,true);
                if (compic(command,"deop")) chanserv_opdeop(client,ppchans,user_regs,false);
                if (compic(command,"invite")) chanserv_invite(client,ppchans,user_regs);
                if (compic(command,"kick")) chanserv_kick(client,ppchans,clients,channels);
                if (compic(command,"unban")) chanserv_unban(client,*ppchans);
        }
        else
                write_sock(client,":%s!%s@%s NOTICE %s :Comando %s desconhecido. \"/%s help\" para ajuda.\r\n",
                           CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,command,CHANSERV_NAME);

        free(command);
}

static bool chanserv_iscommand(char *cmd)
{
        if (compic(cmd,"register")) return true;
        if (compic(cmd,"identify")) return true;
        if (compic(cmd,"info")) return true;
        if (compic(cmd,"access")) return true;
        if (compic(cmd,"op")) return true;
        if (compic(cmd,"deop")) return true;
        if (compic(cmd,"invite")) return true;
        if (compic(cmd,"kick")) return true;
        if (compic(cmd,"unban")) return true;

        return false;
}

void save_chanreg(CHANS *chan)
{

}

bool is_akicked(CLIENTS *client, struct chan_akicks *akicks)
{
        while (akicks != NULL)
        {
                if (akicks->mask_type == MASK_NICK && compic(akicks->nick,client->nick))
                        return true;

                if (akicks->mask_type == MASK_IP && compic(akicks->md5_ip,client->md5_ip))
                        return true;

                if (akicks->mask_type == MASK_BOTH && compic(akicks->md5_ip,client->md5_ip) && compic(akicks->nick,client->nick))
                        return true;

                if (akicks->mask_type == MASK_EVERYBODY)
                        return true;

                akicks=akicks->next;
        }

        return false;
}

veryshort get_user_access_level(struct access_users *ausers, char *nick)
{
        while (ausers != NULL)
        {
                if (compic(ausers->nick,nick))
                {
                        if (ausers->founder_access)
                                return FOUNDER_LEVEL;
                        return ausers->level;
                }
                ausers=ausers->next;
        }

        return 0;
}

struct access_users *get_user_access(struct access_users *ausers, char *nick)
{
        while (ausers != NULL)
        {
                if (compic(ausers->nick,nick))
                        return ausers;
                ausers=ausers->next;
        }

        return NULL;
}
